////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    RedSandsGuts.cpp                                             //
//  Developer:   Tommy Riddle                                                 //
//  Create date: 10/07/2005                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Red Sands.                    //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2005 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "RedSandsGuts.h"

////////////////////////////////////////////////////////////////////////////
/// RedSandsGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Nine, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_NINE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_NINE.
////////////////////////////////////////////////////////////////////////////
RedSandsGuts::SpinResult RedSandsGuts::m_aLineWinSpinResults[NUM_VALID_ICONS][6] = 
{
// Number of icons:		  0				1					2					3					4					5
/* ICON_NINE */			{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,			EVT_THREE_NINE,		EVT_FOUR_NINE,		EVT_FIVE_NINE		},
/* ICON_TEN */			{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,			EVT_THREE_TEN,		EVT_FOUR_TEN,		EVT_FIVE_TEN		},
/* ICON_JACK */			{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,			EVT_THREE_JACK,		EVT_FOUR_JACK,		EVT_FIVE_JACK		},
/* ICON_QUEEN */		{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,			EVT_THREE_QUEEN,	EVT_FOUR_QUEEN,		EVT_FIVE_QUEEN		},
/* ICON_KING */			{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,			EVT_THREE_KING,		EVT_FOUR_KING,		EVT_FIVE_KING		},
/* ICON_ACE */			{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,			EVT_THREE_ACE,		EVT_FOUR_ACE,		EVT_FIVE_ACE		},
/* ICON_ECHIDNA */		{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_ECHIDNA,	EVT_THREE_ECHIDNA,	EVT_FOUR_ECHIDNA,	EVT_FIVE_ECHIDNA	},
/* ICON_WOMBAT */		{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_WOMBAT,		EVT_THREE_WOMBAT,	EVT_FOUR_WOMBAT,	EVT_FIVE_WOMBAT		},
/* ICON_KOALA */		{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_KOALA,		EVT_THREE_KOALA,	EVT_FOUR_KOALA,		EVT_FIVE_KOALA		},
/* ICON_MOONSCAPE */	{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_KANGAROO */		{ EVT_LOSER,	EVT_ONE_KANGAROO,	EVT_TWO_KANGAROO,	EVT_THREE_KANGAROO,	EVT_FOUR_KANGAROO,	EVT_FIVE_KANGAROO	}
};

////////////////////////////////////////////////////////////////////////////
/// RedSandsGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since Moonscapes are the only
/// scatter win.  So to get the SpinResult for 5 Moonscapes, use the
/// following code snippet:
///
///		SpinResult result = m_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_MOONSCAPE.
////////////////////////////////////////////////////////////////////////////
RedSandsGuts::SpinResult RedSandsGuts::m_aScatterWinSpinResults[6] = 
{
	// ICON_COIN			   Number of Icons
	EVT_LOSER,				// 0
	EVT_SCATTER_NEAR_MISS,	// 1
	EVT_TWO_MOONSCAPE,		// 2
	EVT_THREE_MOONSCAPE,	// 3
	EVT_FOUR_MOONSCAPE,		// 4
	EVT_FIVE_MOONSCAPE		// 5
};
        
////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::RedSandsGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
RedSandsGuts::RedSandsGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::~RedSandsGuts()
///
/// Destructor for RedSandsGuts. 
////////////////////////////////////////////////////////////////////////////
RedSandsGuts::~RedSandsGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::SpinResult RedSandsGuts::getKangarooLineWinResult( uint8* stops ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Kangaroo line win payout.
////////////////////////////////////////////////////////////////////////////
RedSandsGuts::SpinResult RedSandsGuts::getKangarooLineWinResult( uint8* stops ) const
{
	// Count how many Kangaroos we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Kangaroo...
		if ( getIcon( getGameModeId(), i, stops[i] )->iconid != ICON_KANGAROO )
		{
			// then we have our total.
			break;
		}
    }

	// Use the array to look up the spin result for i Kangaroos.
	return lookupLineWinSpinResult( ICON_KANGAROO, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::SpinResult RedSandsGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult				A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
RedSandsGuts::SpinResult RedSandsGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 testIcon, bool& bSubstitution ) const
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Kangaroo...
		if ( currentIcon == ICON_KANGAROO )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		// Otherwise if the test icon is a Kangaroo...
		else if ( testIcon == ICON_KANGAROO )
		{
			// then we still have a substitution.  In this case the line
			// started out with the substitution symbol.
			bSubstitution = true;

			// We have now found our first icon that is not a substitution symbol.
			// All of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
    }

	// If the test icon is still a Kangaroo...
	if ( testIcon == ICON_KANGAROO )
	{
		// then we must have 5 Kangaroo.
		ASSERT( i == 5 );

		// In this case we can't give them the substitution bonus.
		bSubstitution = false;
	}

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool RedSandsGuts::doesKangarooLineWinPayMore( SpinResult srKangaroo, SpinResult srOther, bool bSubstitution ) const
///
/// @param   srKangaroo		The spin result to calculate the Kangaroo payout for.
/// @param   srOther		The spin result to calculate the other payout for.
/// @param   bSubstitution	Set if we should use the substitution multiplier with the other payout.
///
/// @return	 bool			True if the Kangaroo win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool RedSandsGuts::doesKangarooLineWinPayMore( SpinResult srKangaroo,
	SpinResult srOther, bool bSubstitution ) const
{
	// Lookup the payout for the Kangaroo line win.
	long nKangarooLineWinPayout = getPayout( srKangaroo )->payoutAmount;

	// See what the total payout for the other line win is without the substitution
	// multiplier (if any).
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// If we had a substitution...
	if ( bSubstitution )
	{
		// then factor in the multiplier.
		nOtherLineWinPayout *= MULTIPLIER_SUBSTITUTION;
	}

	// Return true if the Kangaroo payout is bigger.
    return ( nKangarooLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// @return	 uint8      A payout from the SpinResult enumeration as a uint8.    
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// If we are in re-spin mode...
	if ( getGameModeId() == MODE_RESPIN )
	{
		// then we don't allow line wins.
		rtnresult = EVT_LOSER;

		// Exit out early.
		return;
	}

	// Track the Kangaroo result separately since it might be changed to a substitution.
	SpinResult srKangarooLineWinResult = getKangarooLineWinResult( stops );

	// Now see what the payout is if we use any Kangaroos we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// See what the spin result is if we used any Kangaroos as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, testIcon, bSubstitution );

	// Use the larger payout between the regular Kangaroo evaluation compared to
	// the evaluation with the possible Kangaroo substitution.
    if ( doesKangarooLineWinPayMore( srKangarooLineWinResult,
		srResultWithPossibleSubstitution, bSubstitution ) )
	{
		// The Kangaroos by themselves pay out more so use the Kangaroo result.
		rtnresult = srKangarooLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;

		// If we did actually have a substitution and a winning spin...
		if ( bSubstitution && srResultWithPossibleSubstitution != EVT_LOSER )
		{
			// then add a note and change the multiplier.
			spinline->addNote( NOTE_SUBSTITUTION );
			multiplier = MULTIPLIER_SUBSTITUTION;
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		RedSandsGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) || 
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn		RedSandsGuts::getScatterWinResult()
///
/// @param   spin				The spin we are evaluating.
/// @param   et					The direction that we should use when we evaluate the scatter.
/// @param   scatterPositions	An output parameter that contains a bit mask for the icon positions.
/// @param	 bSubstitition		An output parameter that is set to true if a substitution symbol was used.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
///	This function looks for scatter wins.  This game supports "left to right"
/// and "right to left".  It also supports "any" during the respin feature.
////////////////////////////////////////////////////////////////////////////
RedSandsGuts::SpinResult RedSandsGuts::getScatterWinResult( SlotSpin* spin, EvalType et, int32& scatterPositions, bool& bSubstitution ) const
{
	// Keep up with how many scatter icons we found.
	uint8 numScatterIcons = 0;

	// Keep up with the direction we are going.
	int nDirection;

	// We need a place to keep up with the starting reel since we can go
	// in either direction.
	int nStartReel;

	// If we are going from left to right or we are paying out on any...
	if ( et == etLeftToRight || et == etAny )
	{
		// then start at reel 0.
		nStartReel = 0;

		// We will need to increment the reel as we iterate through the loop.
		nDirection = 1;
	}
	else
	{
		// Otherwise we must be going from right to left.
		ASSERT( et == etRightToLeft );

		// Start on the last reel.
		nStartReel = getReelCount() - 1;

		// We will need to decrement the reel as we iterate through the loop.
		nDirection = -1;
	}

	// Intitialize the substitution out parameter.
	bSubstitution = false;

	// Loop through each reel.
  	for( int i = 0; i < getReelCount(); i++ )
	{
		// Figure out what our current reel is for this iteration.
		int reel = nStartReel + ( i * nDirection );

		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Keep up with whether or not we found a scatter icon on this reel.
		bool bFoundScatterIconOnReel = false;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is a Moonscape...
			if ( currentIcon == ICON_MOONSCAPE )
			{
				// Set the flag so that we know we found a scatter icon on this reel.
				bFoundScatterIconOnReel = true;
			}
			// Otherwise if the icon is a Kangaroo and we are paying out for any...
			else if ( currentIcon == ICON_KANGAROO && et == etAny )
			{
				// then set the flag so that we know we found a scatter icon on this reel.
				bFoundScatterIconOnReel = true;

				// In this mode Kangaroo's substitute for Moonscapes.
				bSubstitution = true;
			}

			// If we found a scatter icon...
			if ( bFoundScatterIconOnReel )
			{
				// then increase the scatter icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;

				// Break out now.
				break;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}

		// If we are not paying out for Any and
		// we didn't find a scatter icon on the reel...
		if ( et != etAny && !bFoundScatterIconOnReel )
		{
			// then we are done.
			break;
		}
	}

	// Use the array to look up the spin result in the current game mode.
	return lookupScatterWinSpinResult( numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions, bool bSubstitution )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Default to no multiplier.
		int32 nMultiplier = 1;

		// If we have a substitution...
		if ( bSubstitution )
		{
			// then factor in the multiplier.
			nMultiplier = MULTIPLIER_SUBSTITUTION;
		}

		// Add the payout to the total win for this spin.
		increaseTotalWin( nPayout * nMultiplier );
    
		// Add a scatter to the spin.
		SlotScatter* pScatter = spin->addScatter( scatterPositions,
			nPayout * getBetPerLine(), nMultiplier, false, srScatterResult, 0 );

		// If we have a substitution...
		if ( bSubstitution )
		{
			// then add the substitution note.
			pScatter->addNote( NOTE_SUBSTITUTION );

			// We should be in re-spin mode.
			ASSERT( getGameModeId() == MODE_RESPIN );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::evaluateFeatureTrigger()
///
/// This function determines if the feature is triggered.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::evaluateFeatureTrigger( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// We need to keep up with the scatter icons used for the feature trigger sounds.
	int32 nTriggerSoundPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is a Moonscape...
			if ( currentIcon == ICON_MOONSCAPE )
			{
				// Set the bit in the position mask for this stop.
				nTriggerSoundPositions |= 1 << position;

				// No need to look at the other rows.
				break;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Add a scatter for the feature trigger sounds.
	spin->addScatter( nTriggerSoundPositions, 0, 0, false,
		EVT_FEATURE_TRIGGER_SCATTER, 0 );

	// We need a spin result for the feature trigger.
	SpinResult srFeatureTrigger;

	// We need a place to keep up with how many re-spins to award.
	int32 nRespins = 0;

	// If the scatter result was a loser...
	if ( srScatterResult == EVT_LOSER )
	{
		// then we are done so just return now.
		return;
	}
	// Otherwise if the scatter result was a near miss...
	else if ( srScatterResult == EVT_SCATTER_NEAR_MISS )
	{
		// then we will also have a near miss for the feature.
		srFeatureTrigger = EVT_FEATURE_NEAR_MISS;
	}
	// Otherwise...
	else
	{
		// we hit the re-spin feature.
		srFeatureTrigger = EVT_RESPIN_FEATURE;

		// Make sure that we really did have the correct scatter icons.
		ASSERT( srScatterResult == EVT_TWO_MOONSCAPE ||
			srScatterResult == EVT_THREE_MOONSCAPE || 
			srScatterResult == EVT_FOUR_MOONSCAPE ||
			srScatterResult == EVT_FIVE_MOONSCAPE ||
			srScatterResult == EVT_TWO_MOONSCAPE_XXX_TWO_MOONSCAPE );

		// Make sure we are in re-spin mode.
		setGameModeId( MODE_RESPIN );

		// The number of re-spins is selected from the probability table.
		nRespins = getRandomValueFromProbabilityTable();

		if(m_bDebugSpinsMode)
		{
			if(g_nDebugRespins > 0)
			{
				nRespins = g_nDebugRespins;
				g_nDebugRespins = -1;
			}
		}

		// Add the re-spins.
		incrementFreeSpins( nRespins );

		// We need to see which reels had scatters on them.
		// This array of masks will extract the scatter position
		// bits for the given reel.  So to see if reel 2 had any
		// scatters on it, just do this:
		//   scatterPositions & nScatterMask[ 2 ]
		int32 nScatterMask[] = 
		{
			SCATTER_REEL_0_MASK,
			SCATTER_REEL_1_MASK,
			SCATTER_REEL_2_MASK,
			SCATTER_REEL_3_MASK,
			SCATTER_REEL_4_MASK
		};

		// Make sure we have an entry in the scatter mask array
		// for each reel in the game.
		ASSERT( countof( nScatterMask ) == getReelCount() );

		// An extra check in debug builds.
		#ifdef _DEBUG
			uint8 nReelsHeld = 0;
		#endif

		// Get the stops for the spin.
        const uint8* pStops = spin->getStops();

		// Loop through all of the reels.
		for ( uint8 n = 0; n < getReelCount(); n++ )
		{
			// If this reel has a scatter icon on it...
			if ( scatterPositions & nScatterMask[ n ] )
			{
				// Then set the hold stop to be the current stop for the reel.
				setReelHoldStop( n, pStops[ n ] );

				// An extra check in debug builds.
				#ifdef _DEBUG
					nReelsHeld++;
				#endif
			}
		}

		// Make sure that we found at least 2 reels that are held.
		ASSERT( nReelsHeld >= 2 );

		// Sanity check.
		ASSERT( nReelsHeld <= getReelCount() );
	}

	// Add a scatter to the spin for the near miss or the feature trigger.
	spin->addScatter( scatterPositions, 0, 0, false,
		srFeatureTrigger, nRespins );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Keep up with whether or not we have a substitution.
	bool bSubstitution = false;

	// If we are in the normal game mode...
	if ( getGameModeId() == MODE_NORMAL )
	{
		// then see what the payout is going left to right.
		// Declare a local variable to track the scatter positions.
		int32 nLeftToRightScatterPositions = 0;

		// See what the spin result is going left to right.
		SpinResult srLeftToRightResult = getScatterWinResult( spin,
			etLeftToRight, nLeftToRightScatterPositions, bSubstitution );

		// We should never have a substitution for left to right scatters.
		ASSERT( !bSubstitution );

		// Now see what the payout is going right to left.
		// Declare a local variable to track the scatter positions.
		int32 nRightToLeftScatterPositions = 0;

		// See what the spin result is going right to left.
		SpinResult srRightToLeftResult = getScatterWinResult( spin,
			etRightToLeft, nRightToLeftScatterPositions, bSubstitution );

		// We should never have a substitution for right to left scatters.
		ASSERT( !bSubstitution );

		// Give them the left to right scatter.
		evaluateScatterWin( spin, srLeftToRightResult, nLeftToRightScatterPositions, false );

		// Special handling for a simultaneous left to right scatter and
		// right to left scatter.
		if ( srLeftToRightResult == EVT_TWO_MOONSCAPE &&
			srRightToLeftResult == EVT_TWO_MOONSCAPE )
		{
			// Combine these two scatters into one special scatter.
			evaluateFeatureTrigger( spin, EVT_TWO_MOONSCAPE_XXX_TWO_MOONSCAPE,
				nLeftToRightScatterPositions | nRightToLeftScatterPositions );
		}
		// Otherwise...
		else
		{
			// see if they triggered the feature with just the left to right scatter.
			evaluateFeatureTrigger( spin, srLeftToRightResult, nLeftToRightScatterPositions );
		}

		// If the left to right result was not five Moonscapes...
		if ( srLeftToRightResult != EVT_FIVE_MOONSCAPE )
		{
			// then also give them the right to left scatter.
			evaluateScatterWin( spin, srRightToLeftResult, nRightToLeftScatterPositions, false );

			// If the left to right scatter was not two Moonscapes...
			if ( srLeftToRightResult != EVT_TWO_MOONSCAPE )
			{
				// then see if they triggered the feature with just the right to left scatter.
				evaluateFeatureTrigger( spin, srRightToLeftResult, nRightToLeftScatterPositions );
			}
		}
	}
	// Otherwise...
	else
	{
		// we must be in re-spin mode.
		ASSERT( getGameModeId() == MODE_RESPIN );

		// See what the payout is for any.
		// Declare a local variable to track the scatter positions.
		int32 nAnyScatterPositions = 0;

		// See what the spin result is for any.
		SpinResult srAnyResult = getScatterWinResult( spin,
			etAny, nAnyScatterPositions, bSubstitution );

		// Give them the any scatter.
		evaluateScatterWin( spin, srAnyResult, nAnyScatterPositions, bSubstitution );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 RedSandsGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 scatter icons.
	ASSERT( countof( m_aScatterWinSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_RESPIN ) );

	// Finally just check to see if the gut is within the right range.
    return ( getGutsId() >= 2000 && getGutsId() < 2100 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::resetReelHoldStops()
///
/// This function resets all of the reel hold stops in preparation for a
/// new spin.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::resetReelHoldStops()
{
	// Just set the sub-game data back to -1.
    m_subGame->update( SGDIDX_REEL_HOLD_STOP_1, -1 );
    m_subGame->update( SGDIDX_REEL_HOLD_STOP_2, -1 );
    m_subGame->update( SGDIDX_REEL_HOLD_STOP_3, -1 );
    m_subGame->update( SGDIDX_REEL_HOLD_STOP_4, -1 );
    m_subGame->update( SGDIDX_REEL_HOLD_STOP_5, -1 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::getReelHoldStop()
///
/// Accessor function to get the stop position to hold for the passed in
/// reel.
////////////////////////////////////////////////////////////////////////////
int32 RedSandsGuts::getReelHoldStop( uint8 nReel )
{
	// Simple bounds check.
	ASSERT( nReel < getReelCount() );

	// Just return the sub-game data for the reel.
    return m_subGame->getData( SGDIDX_REEL_HOLD_STOP_1 + nReel );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::setReelHoldStop()
///
/// Accessor function to set the stop position to hold for the passed in
/// reel.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::setReelHoldStop( uint8 nReel, int32 nStop )
{
	// Simple bounds check.
	ASSERT( nReel < getReelCount() );

	// Update the sub-game data.
    m_subGame->update( SGDIDX_REEL_HOLD_STOP_1 + nReel, nStop );

	// If we are holding this reel...
	if ( nStop != -1 )
	{
		// then log it in the feature data for an audit trail.
		m_feature->update( SGDIDX_REEL_HOLD_STOP_1 + nReel, nStop, true );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to alter the reel stops for the respin feature.
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::preSpinClassifyResults( SlotSpin* spin )
{
	SlotGuts::preSpinClassifyResults(spin);

	// If we are in normal mode...
	if ( getGameModeId() == MODE_NORMAL )
	{
		// then reset all of the reel hold stops.
		resetReelHoldStops();
	}
	// Otherwise...
    else
    {
		// we must be in re-spin mode.
		ASSERT( getGameModeId() == MODE_RESPIN );

		// We will need to hold some of the reel stops, which
		// means we need to cast away the constness of the stops.
        uint8* pStops = const_cast< uint8* >( spin->getStops() );
		
		// An extra check in debug builds.
		#ifdef _DEBUG
			uint8 nReelsHeld = 0;
		#endif

		// Loop through all of the reels.
		for ( uint8 n = 0; n < getReelCount(); n++ )
		{
			// Get the stop we are supposed to use for this reel, if any.
			int32 nStop = getReelHoldStop( n );

			// If we are holding this reel at a specific stop...
			if ( nStop != -1 )
			{
				// then store the stop in the spin.
				pStops[ n ] = nStop;

				// An extra check in debug builds.
				#ifdef _DEBUG
					nReelsHeld++;
				#endif
			}
		}

		// Make sure that we found at least 2 reels that are held.
		ASSERT( nReelsHeld >= 2 );

		// Sanity check.
		ASSERT( nReelsHeld <= getReelCount() );
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 RedSandsGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=0;
	pStops[1]=3;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}


////////////////////////////////////////////////////////////////////////////
/// Debug spins functionality, can control how many respins are triggered
///
////////////////////////////////////////////////////////////////////////////
void RedSandsGuts::initDebugFunctionMap()
{
	SlotGuts::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_1, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins1) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_2, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins2) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_3, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins3) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_4, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins4) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_5, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins5) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_6, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins6) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_7, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins7) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_8, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins8) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_9, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins9) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RESPINS_10, 
		new DebugStopFunc<RedSandsGuts>(this, &RedSandsGuts::DebugFeature_Respins10) );
}

int RedSandsGuts::g_nDebugRespins = -1;

void RedSandsGuts::DebugFeature_Respins1( SlotSpin *spin )
{
	g_nDebugRespins = 1;
}
void RedSandsGuts::DebugFeature_Respins2( SlotSpin *spin )
{
	g_nDebugRespins = 2;
}
void RedSandsGuts::DebugFeature_Respins3( SlotSpin *spin )
{
	g_nDebugRespins = 3;
}
void RedSandsGuts::DebugFeature_Respins4( SlotSpin *spin )
{
	g_nDebugRespins = 4;
}
void RedSandsGuts::DebugFeature_Respins5( SlotSpin *spin )
{
	g_nDebugRespins = 5;
}
void RedSandsGuts::DebugFeature_Respins6( SlotSpin *spin )
{
	g_nDebugRespins = 6;
}
void RedSandsGuts::DebugFeature_Respins7( SlotSpin *spin )
{
	g_nDebugRespins = 7;
}
void RedSandsGuts::DebugFeature_Respins8( SlotSpin *spin )
{
	g_nDebugRespins = 8;
}
void RedSandsGuts::DebugFeature_Respins9( SlotSpin *spin )
{
	g_nDebugRespins = 9;
}
void RedSandsGuts::DebugFeature_Respins10( SlotSpin *spin )
{
	g_nDebugRespins = 10;
}